﻿using System;
using System.Collections.Generic;
//using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;

namespace ESpace
{
    public class EBmp
    {
       
        public enum Sposob { evenly, optimize };

        struct LBmp
        {
            public IntPtr ptr;
            public BitmapData bmpData;
            public byte[] rgbValues;
        }

        #region *** внутренние функции **************************

        static LBmp LockBmp(Bitmap bmp, bool bCopy)
        {
            LBmp l = new LBmp();
            // Lock the bitmap's bits.  
            Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
            l.bmpData = bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, bmp.PixelFormat);

            // Get the address of the first line.
            l.ptr = l.bmpData.Scan0;

            // Declare an array to hold the bytes of the bitmap.
            int bytes = l.bmpData.Stride * bmp.Height;
            l.rgbValues = new byte[bytes];

            if (bCopy)
            {
                // Копируем битмап в массив байт.
                System.Runtime.InteropServices.Marshal.Copy(l.ptr, l.rgbValues, 0, bytes);
            }
            return l;
        }

        static void UnLockBmpCopy(Bitmap bmp, LBmp l)
        {
            // Copy the RGB values back to the bitmap
            System.Runtime.InteropServices.Marshal.Copy(l.rgbValues, 0, l.ptr, l.rgbValues.Length);
            // Unlock the bits
            bmp.UnlockBits(l.bmpData);
        }

        #endregion *** внутренние функции **************************

        #region *** TO **********************************************************

        /// <summary>
        /// Битмап в матрицу цветов
        /// </summary>
        public static Color[,] BmpToClrMtr(Bitmap bmp)
        {
            int pW = bmp.Width;
            int pH = bmp.Height;
            Color[,] mtr = new Color[pW, pH];
            // Блокируем битмап с копированием его данных в массив
            LBmp l = LockBmp(bmp, true);

            // заполняем матрицу цветов 
            for (int j = 0; j < pH; j++)
                for (int i = 0; i < pW; i++)
                {
                    int x = i * 4 + j * l.bmpData.Stride;
                    mtr[i, j] = Color.FromArgb(l.rgbValues[x + 3], l.rgbValues[x + 2], l.rgbValues[x + 1], l.rgbValues[x]);
                }

            // Разблокируем битмап без копирования
            bmp.UnlockBits(l.bmpData);
            return mtr;
        }

        /// <summary>
        /// Битмап в матрицу оттенков серого 256
        /// </summary>
        public static byte[,] BmpToGrayMtr(Bitmap bmp)
        {
            return BmpToGrayMtr(bmp, Sposob.optimize);
        }
        /// <summary>
        /// Битмап в матрицу оттенков серого 256
        /// (ssb - способ преобразования в полутоновое)
        /// </summary>
        public static byte[,] BmpToGrayMtr(Bitmap bmp, Sposob ssb)
        {
            int pW = bmp.Width;
            int pH = bmp.Height;
            byte[,] mtr = new byte[pW, pH];

            // Блокирует битмап с копированием
            LBmp l = LockBmp(bmp, true);

            // заполняем массив цветов согласно выбранному алгоритму
            if (ssb == Sposob.evenly)
            {
                // заполняем массив цветов 
                for (int j = 0; j < pH; j++)
                    for (int i = 0; i < pW; i++)
                    {
                        int x = i * 4 + j * l.bmpData.Stride;

                        double R = l.rgbValues[x + 2];
                        double G = l.rgbValues[x + 1];
                        double B = l.rgbValues[x];
                        mtr[i, j] = Convert.ToByte(0.3333 * R + 0.3333 * G + 0.3333 * B);
                    }
            }
            else
            {
                for (int j = 0; j < pH; j++)
                    for (int i = 0; i < pW; i++)
                    {
                        int x = i * 4 + j * l.bmpData.Stride;
                        double R = l.rgbValues[x + 2];
                        double G = l.rgbValues[x + 1];
                        double B = l.rgbValues[x];
                        mtr[i, j] = Convert.ToByte(0.3 * R + 0.59 * G + 0.11 * B);
                    }
            }

            // Разблокируем битмап без копирования
            bmp.UnlockBits(l.bmpData);
            return mtr;
        }

        /// <summary>
        /// Битмап в матрицу черно-белого (true - черный)
        /// </summary>
        public static bool[,] BmpToBWMtr(Bitmap bmp)
        {
            return BmpToBWMtr(bmp, Sposob.optimize, 128);
        }
        /// <summary>
        /// Битмап в матрицу черно-белого (true - черный)
        /// (porog - порог, ниже которого будет черный)
        /// </summary>
        public static bool[,] BmpToBWMtr(Bitmap bmp, byte porog)
        {
            return BmpToBWMtr(bmp, Sposob.optimize, porog);
        }
        /// <summary>
        /// Битмап в матрицу черно-белого
        /// (ssb - способ преобразования в полутоновое, перед преобразованием в чеоно-белое)
        /// </summary>
        public static bool[,] BmpToBWMtr(Bitmap bmp, Sposob ssb)
        {
            return BmpToBWMtr(bmp, ssb, 128);
        }
        /// <summary>
        /// Битмап в матрицу черно-белого (true - черный)
        /// (ssb - способ преобразования в полутоновое, перед преобразованием в чеоно-белое, porog - порог, ниже которого будет черный)
        /// </summary>
        public static bool[,] BmpToBWMtr(Bitmap bmp, Sposob ssb, byte porog)
        {
            int pW = bmp.Width;
            int pH = bmp.Height;
            bool[,] mtr = new bool[pW, pH];
            
            // Блокирует битмап с копированием
            LBmp l = LockBmp(bmp, true);

            // заполняем массив цветов согласно выбранному алгоритму
            if (ssb == Sposob.evenly)
            {
                // заполняем массив цветов 
                for (int j = 0; j < pH; j++)
                    for (int i = 0; i < pW; i++)
                    {
                        int x = i * 4 + j * l.bmpData.Stride;

                        double R = l.rgbValues[x + 2];
                        double G = l.rgbValues[x + 1];
                        double B = l.rgbValues[x];
                        mtr[i, j] = (Convert.ToByte(0.3333 * R + 0.3333 * G + 0.3333 * B) < porog);
                    }
            }
            else
            {
                for (int j = 0; j < pH; j++)
                    for (int i = 0; i < pW; i++)
                    {
                        int x = i * 4 + j * l.bmpData.Stride;
                        double R = l.rgbValues[x + 2];
                        double G = l.rgbValues[x + 1];
                        double B = l.rgbValues[x];
                        mtr[i, j] = (Convert.ToByte(0.3 * R + 0.59 * G + 0.11 * B) < porog);
                    }
            }

            // Разблокируем битмап
            bmp.UnlockBits(l.bmpData);
            return mtr;
        }
        
        /// <summary>
        /// Матрицу цветов в битмап
        /// </summary>
        public static Bitmap ClrMtrToBmp(Color[,] mtr)
        {
            int pW = mtr.GetUpperBound(0)+1;
            int pH = mtr.GetUpperBound(1)+1;
            Bitmap bmp = new Bitmap(pW, pH);
            
            // Блокирует битмап без копирования
            LBmp l = LockBmp(bmp,false);

            // копируем в массив матрицу цветов
            for (int j = 0; j < pH; j++)
                for (int i = 0; i < pW; i++)
                {
                    int x = i * 4 + j * l.bmpData.Stride;

                    l.rgbValues[x] = mtr[i, j].B;
                    l.rgbValues[x + 1] = mtr[i, j].G;
                    l.rgbValues[x + 2] = mtr[i, j].R;
                    l.rgbValues[x + 3] = mtr[i, j].A;
                }

            // Разблокируем битмап с копированием
            UnLockBmpCopy(bmp,l);
            return bmp;
        }
        
        /// <summary>
        /// Матрицу цветов оттенков серого 256 в битмап
        /// </summary>
        public static Bitmap GrayMtrToBmp(byte[,] mtr)
        {
            int pW = mtr.GetUpperBound(0) + 1;
            int pH = mtr.GetUpperBound(1) + 1;
            Bitmap bmp = new Bitmap(pW, pH);
            
            // Блокирует битмап без копирования
            LBmp l = LockBmp(bmp, false);

            // копируем в массив матрицу
            for (int j = 0; j < pH; j++)
                for (int i = 0; i < pW; i++)
                {
                    int x = i * 4 + j * l.bmpData.Stride;
                    l.rgbValues[x] = mtr[i, j];
                    l.rgbValues[x + 1] = mtr[i, j];
                    l.rgbValues[x + 2] = mtr[i, j];
                    l.rgbValues[x + 3] = 255;
                }

            // Разблокируем битмап с копированием
            UnLockBmpCopy(bmp, l);
            return bmp;
        }
        
        /// <summary>
        /// Матрицу черно-белого в битмап
        /// </summary>
        public static Bitmap BWMtrToBmp(bool[,] mtr)
        {
            int pW = mtr.GetUpperBound(0) + 1;
            int pH = mtr.GetUpperBound(1) + 1;
            Bitmap bmp = new Bitmap(pW, pH);

            // Блокирует битмап без копирования
            LBmp l = LockBmp(bmp, false);

            // копируем в массив матрицу
            for (int j = 0; j < pH; j++)
                for (int i = 0; i < pW; i++)
                {
                    int x = i * 4 + j * l.bmpData.Stride;
                    byte c = 255;
                    if (mtr[i, j]) c = 0;
                    l.rgbValues[x] = c;
                    l.rgbValues[x + 1] = c;
                    l.rgbValues[x + 2] = c;
                    l.rgbValues[x + 3] = 255;
                }

            // Разблокируем битмап с копированием
            UnLockBmpCopy(bmp,l);
            return bmp;
        }

        /// <summary>
        /// преобразовать матрицу цветов в матрицу полутонового изображения
        /// </summary>
        static public bool ColorToGray(Color[,] mtrClr, byte[,] mtrGray)
        {
            return ColorToGray(mtrClr, mtrGray, Sposob.optimize);
        }
        /// <summary>
        /// преобразовать матрицу цветов в матрицу полутонового изображения
        /// (ssb - способ преобразования в полутоновое)
        /// </summary>
        static public bool ColorToGray(Color[,] mtrClr, byte[,] mtrGray, Sposob ssb)
        {
            int w = mtrGray.GetUpperBound(0);
            int h = mtrGray.GetUpperBound(1);
            if (w != mtrClr.GetUpperBound(0) | h != mtrClr.GetUpperBound(1)) return false;
            
            // заполняем массив согласно выбранному алгоритму
            if (ssb == Sposob.evenly)
            {
                // заполняем массив цветов 
                for (int j = 0; j < h; j++)
                    for (int i = 0; i < w; i++)
                    {
                        double R = mtrClr[i,j].R;
                        double G = mtrClr[i, j].G;
                        double B = mtrClr[i, j].B;
                        mtrGray[i, j] = Convert.ToByte(0.3333 * R + 0.3333 * G + 0.3333 * B);
                    }
            }
            else
            {
                for (int j = 0; j < h; j++)
                    for (int i = 0; i < w; i++)
                    {
                        double R = mtrClr[i, j].R;
                        double G = mtrClr[i, j].G;
                        double B = mtrClr[i, j].B;
                        mtrGray[i, j] = Convert.ToByte(0.3 * R + 0.59 * G + 0.11 * B);
                    }
            }
            return true;
        }

        /// <summary>
        /// преобразовать матрицу полутонового изображения в матрицу черно-белого изображения
        /// </summary>
        static public bool GrayToBW(byte[,] mtrGray, bool[,] mtrBW, byte porog)
        {
            int w = mtrGray.GetUpperBound(0);
            int h = mtrGray.GetUpperBound(1);
            if (w != mtrBW.GetUpperBound(0) | h != mtrBW.GetUpperBound(1)) return false;
            for (int j = 0; j < h; j++)
                for (int i = 0; i < w; i++)
                {
                    mtrBW[i, j] = ((mtrGray[i, j]) < porog);
                }
            return true;
        }

        /// <summary>
        /// преобразовать матрицу черно-белого изображения в матрицу полутонового изображения
        /// </summary>
        static public bool BWToGray(bool[,] mtrBW, byte[,] mtrGray)
        {
            int w = mtrGray.GetUpperBound(0);
            int h = mtrGray.GetUpperBound(1);
            if (w != mtrBW.GetUpperBound(0) | h != mtrBW.GetUpperBound(1)) return false;
            for (int j = 0; j < h; j++)
                for (int i = 0; i < w; i++)
                {
                    mtrGray[i, j] = 255;
                    if (mtrBW[i, j]) mtrGray[i, j] = 0;
                }
            return true;
        }

        #endregion *** TO **********************************************************

        #region *** Convert **********************************************************

        public static Bitmap ConvertToGray(Bitmap bmp)
        {
            return GrayMtrToBmp(BmpToGrayMtr(bmp));
        }

        public static Bitmap ConvertToBW(Bitmap bmp)
        {
            return BWMtrToBmp(BmpToBWMtr(bmp));
        }
        public static Bitmap ConvertToBW(Bitmap bmp, byte porog)
        {
            return BWMtrToBmp(BmpToBWMtr(bmp, porog));
        }
        public static Bitmap ConvertToBW(Bitmap bmp, Sposob ssb)
        {
            return BWMtrToBmp(BmpToBWMtr(bmp, ssb));
        }
        public static Bitmap ConvertToBW(Bitmap bmp, Sposob ssb, byte porog)
        {
            return BWMtrToBmp(BmpToBWMtr(bmp, ssb,porog));
        }

        /// <summary>
        /// преобразовать в 32-битный формат
        /// </summary>
        static public Bitmap ConvertBMPTo32bit(Bitmap bmpSource)
        {
            if (bmpSource.PixelFormat == PixelFormat.Format32bppArgb) return bmpSource;

            Bitmap bmp32 = new Bitmap(bmpSource.Width, bmpSource.Height, PixelFormat.Format32bppArgb);
            Graphics g = Graphics.FromImage(bmp32);
            g.DrawImage(bmpSource, 0, 0, bmpSource.Width, bmpSource.Height);
            return bmp32;
        }

        #endregion *** Convert **********************************************************

        #region *** Resize **********************************************************

        /// <summary>
        /// Изменить размер
        /// </summary>
        /// <param name="sBmp">исходная картинка</param>
        /// <param name="w">новая ширина</param>
        /// <param name="h">новая высота</param>
        /// <returns>измененная картинка</returns>
        static public Bitmap Resize(Bitmap sBmp, int w, int h)
        {
            return new Bitmap(sBmp, w, h);
        }
        /// <summary>
        /// Изменить размер
        /// </summary>
        /// <param name="sBmp">исходная картинка</param>
        /// <param name="percent">процент от исходной картинки</param>
        /// <returns>измененная картинка</returns>
        static public Bitmap Resize(Bitmap sBmp, double percent)
        {
            int w = (int)(sBmp.Width / 100.0 * percent);
            int h = (int)(sBmp.Height / 100.0 * percent);
            return Resize(sBmp, w, h);
        }
        /// <summary>
        /// Изменить размер
        /// </summary>
        /// <param name="sBmp">исходная картинка</param>
        /// <param name="w">новая ширина</param>
        /// <returns>измененная картинка</returns>
        static public Bitmap ResizeW(Bitmap sBmp, int w)
        {
            int h = (int)(sBmp.Height*(w/(double)sBmp.Width));
            return Resize(sBmp, w, h);
        }
        /// <summary>
        /// Изменить размер
        /// </summary>
        /// <param name="sBmp">исходная картинка</param>
        /// <param name="h">новая высота</param>
        /// <returns>измененная картинка</returns>
        static public Bitmap ResizeH(Bitmap sBmp, int h)
        {
            int w = (int)(sBmp.Width * (h / (double)sBmp.Height));
            return Resize(sBmp, w, h);
        }
        /// <summary>
        /// ресайзет картинку так, чтобы не теряя пропорций она занимала всю площадь
        /// </summary>
        /// <param name="sBmp">исходная картинка</param>
        /// <param name="maxW">максимальная ширина</param>
        /// <param name="maxH">максимальная высота</param>
        /// <returns>измененная картинка</returns>
        static public Bitmap ResizeMax(Bitmap sBmp, int maxW, int maxH)
        {
            // выссчитываем процент по X и по Y
            double dX = 100.0 * (maxW / (double)sBmp.Width);
            double dY = 100.0 * (maxH / (double)sBmp.Height);
            // расайзим по максимальному проценту от оригинала

            if (dX > dY) return Resize(sBmp, dX);
            return Resize(sBmp, dY);
        }

        #endregion *** Resize **********************************************************

        #region *** Rotate **********************************************************

        static public Bitmap Rotate180(Bitmap sBmp)
        {
            Color[,] cl = BmpToClrMtr(sBmp);
            int szX = cl.GetUpperBound(0)+1;
            int szY = cl.GetUpperBound(1)+1;
            Color[,] cl1 = new Color[szX,szY];
            // поворачиваем матрицу
            for(int i=0;i<szX;i++)
                for (int j = 0; j < szY; j++)
                {
                    cl1[i, j] = cl[szX - 1 - i, szY - 1 - j];
                }
            return ClrMtrToBmp(cl1);
        }

        static public Bitmap Rotate90Clockwise(Bitmap sBmp)
        {
            Color[,] cl = BmpToClrMtr(sBmp);
            int szX = cl.GetUpperBound(0) + 1;
            int szY = cl.GetUpperBound(1) + 1;
            Color[,] cl1 = new Color[szY, szX];
            // поворачиваем матрицу
            for (int i = 0; i < szX; i++)
                for (int j = 0; j < szY; j++)
                {
                    cl1[j, i] = cl[i, szY - 1 - j];
                }
            return ClrMtrToBmp(cl1);
        }

        static public Bitmap Rotate90Anticlockwise(Bitmap sBmp)
        {
            Color[,] cl = BmpToClrMtr(sBmp);
            int szX = cl.GetUpperBound(0) + 1;
            int szY = cl.GetUpperBound(1) + 1;
            Color[,] cl1 = new Color[szY, szX];
            // поворачиваем матрицу
            for (int i = 0; i < szX; i++)
                for (int j = 0; j < szY; j++)
                {
                    cl1[j, i] = cl[szX - 1 - i, j];
                }
            return ClrMtrToBmp(cl1);
        }

        static public Bitmap FlipVert(Bitmap sBmp)
        {
            Color[,] cl = BmpToClrMtr(sBmp);
            int szX = cl.GetUpperBound(0) + 1;
            int szY = cl.GetUpperBound(1) + 1;
            Color[,] cl1 = new Color[szX, szY];
            // поворачиваем матрицу
            for (int i = 0; i < szX; i++)
                for (int j = 0; j < szY; j++)
                {
                    cl1[i, j] = cl[i, szY - 1 - j];
                }
            return ClrMtrToBmp(cl1);
        }

        static public Bitmap FlipHorz(Bitmap sBmp)
        {
            Color[,] cl = BmpToClrMtr(sBmp);
            int szX = cl.GetUpperBound(0) + 1;
            int szY = cl.GetUpperBound(1) + 1;
            Color[,] cl1 = new Color[szX, szY];
            // поворачиваем матрицу
            for (int i = 0; i < szX; i++)
                for (int j = 0; j < szY; j++)
                {
                    cl1[i, j] = cl[szX-1-i, j];
                }
            return ClrMtrToBmp(cl1);
        }


        #endregion *** Rotate **********************************************************

    }
}
